
package com.naza.rpc.compiler;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Locale;

/**
 * 本地类编译器
 * 
 * @author yl
 */
public class NativeCompiler implements Closeable
{
    private final File tempFolder;

    private final URLClassLoader classLoader;

    public NativeCompiler(File tempFolder)
    {
        this.tempFolder = tempFolder;
        this.classLoader = createClassLoader(tempFolder);
    }

    private static URLClassLoader createClassLoader(File tempFolder)
    {
        try
        {
            URL[] urls = {tempFolder.toURI().toURL()};
            return new URLClassLoader(urls);
        }
        catch (Exception e)
        {
            throw new AssertionError(e);
        }
    }

    public Class<?> compile(String className, String code)
    {
        try
        {
            // 生成本地java文件
            JavaFileObject sourceFile = new StringJavaFileObject(className,
                    code);
            // 编译本地java文件
            compileClass(sourceFile);
            // 加载类至类加载器
            return classLoader.loadClass(className);
        }
        catch (Exception e)
        {
            throw new AssertionError(e);
        }
    }

    /**
     * 编译本地java文件
     * 
     * @param sourceFile
     * @throws IOException
     */
    private void compileClass(JavaFileObject sourceFile) throws IOException
    {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
        StandardJavaFileManager fileManager = null;

        try
        {
            fileManager = compiler.getStandardFileManager(collector,
                    Locale.ROOT, null);
            fileManager.setLocation(StandardLocation.CLASS_OUTPUT,
                    Arrays.asList(tempFolder));
            JavaCompiler.CompilationTask task = compiler.getTask(null,
                    fileManager, collector, null, null,
                    Arrays.asList(sourceFile));
            task.call();
        }
        finally
        {
            fileManager.close();
        }
    }

    @Override
    public void close()
    {
        try
        {
            classLoader.close();
        }
        catch (Exception e)
        {
            throw new AssertionError(e);
        }
    }

    public URLClassLoader getClassLoader()
    {
        return classLoader;
    }
}
